#ifndef __GLF_FSTRING_H_INCLUDED__
#define __GLF_FSTRING_H_INCLUDED__

//TODO finish implementing fstring

#include <glf/config.h>
#include <glf/fstl/fstl.h>
#include <glf/fstl/fallocator.h>

#include <string>

namespace glf {

template< typename T >
struct ci_char_traits : public std::char_traits<T>
{
	static bool eq( T c1, T c2 ) { 
		return toupper(c1) == toupper(c2); 
	}

	static bool ne( T c1, T c2 ){ 
		return !eq(c1, c2); //toupper(c1) != toupper(c2); 
	}

	static bool lt( T c1, T c2 ) { 
		return toupper(c1) < toupper(c2); 
	}

	static int compare( const T* s1, const T* s2, size_t n ) {
		return strnicmp( s1, s2, n );
	}

    static const char* find( const T* s, int n, T a ) {
		while( n-- > 0 && toupper(*s) != toupper(a) ) {
			++s;
		}
		return s;
    }
};

template<int SIZE, typename T=char, typename TRAITS=std::char_traits<T>, typename ALLOC=fixed_alloc<T> >
struct fstring : public std::basic_string<T, TRAITS, ALLOC>
{
	typedef std::basic_string<T, TRAITS, ALLOC> MyParent;
	typedef fstring<SIZE, T, TRAITS, ALLOC> MyType;
	typedef ALLOC MyAlloc;
	static const size_t CAPACITY = SIZE;

	fstring() : 
		MyParent(MyAlloc(m_allocInfo, m_buffer, sizeof(m_buffer)))
#if GLF_CONFIG_DEBUG
		, m_dbgCapacity(SIZE+1)
		, m_dbgElems(reinterpret_cast<T (*)[SIZE+1]>(m_buffer))
#endif
	{
		MyParent::reserve(SIZE);
	}

	fstring(const MyType& s) :
		MyParent(MyAlloc(m_allocInfo, m_buffer, sizeof(m_buffer)))
#if GLF_CONFIG_DEBUG
		, m_dbgCapacity(SIZE+1)
		, m_dbgElems(reinterpret_cast<T (*)[SIZE+1]>(m_buffer))
#endif
	{
		MyParent::reserve(SIZE);
		assign(s);
	}

	fstring(const T* p, size_t num) : 
		MyParent(MyAlloc(m_allocInfo, m_buffer, sizeof(m_buffer)))
#if GLF_CONFIG_DEBUG
		, m_dbgCapacity(SIZE+1)
		, m_dbgElems(reinterpret_cast<T (*)[SIZE+1]>(m_buffer))
#endif
	{
		MyParent::reserve(SIZE);
		assign(p, num);
	}

	fstring(const T* p) : 
		MyParent(MyAlloc(m_allocInfo, m_buffer, sizeof(m_buffer)))
#if GLF_CONFIG_DEBUG
		, m_dbgCapacity(SIZE+1)
		, m_dbgElems(reinterpret_cast<T (*)[SIZE+1]>(m_buffer))
#endif
	{
		MyParent::reserve(SIZE);
		assign(p);
	}


	~fstring() {
		MyParent::~MyParent();
	}

	int sprintf(const char* format, ...) {
		//untested
		va_list args;
		va_start(args, format);

		resize(CAPACITY);
		
		int newLength = vsprintf_s(&operator[](0), capacity(), format, args); 
		
		resize(newLength);

		va_end(args);

		return newLength;
	}

	int append_sprintf(const char* format, ...) {
		va_list args;
		va_start(args, format);

		size_t mySize = size();
		resize(CAPACITY);
		int written = vsprintf_s(&operator[](mySize), capacity()-mySize, format, args);

		resize(mySize + written);

		va_end(args);

		return written;
	}

	MyType& operator=(const MyType& other) {
		if(this != &other) {
			MyParent::operator=(other.c_str());
		}
		//assign(other); //MyParent::operator=(other);
		return *this;
	}



	MyType& operator=(const T* ptr) {
		MyParent::operator=(ptr);
		//assign(ptr);
		return *this;
	}

	MyType& operator=(T c) {
		MyParent::operator=(c);
		//assign(c);
		return *this;
	}
	
	MyType substr(size_t off, size_t count) const {
		return MyType(&operator[](off), count);
	}

	void substr(MyType& other, size_t off, size_t count) const {
		other.assign(&operator[](off), count);
	}

private:
	static void* operator new(size_t s);
	static void* operator new[](size_t s);
	static void  operator delete(void*) {}
	static void  operator delete[](void*){}

	void reserve(size_t);

	fixed_alloc_info m_allocInfo;
	char m_buffer[(SIZE+1) * sizeof(T) + 4];

#if GLF_CONFIG_DEBUG	
	const int m_dbgCapacity;
	T (*m_dbgElems)[SIZE+1];
#endif

};

//------------------------------------------------------------------------------
// 
//------------------------------------------------------------------------------

typedef fstring<32, char>		fstring32;
typedef fstring<32, wchar_t>	fwstring32;

typedef fstring<64, char>		fstring64;
typedef fstring<64, wchar_t>	fwstring64;

typedef fstring<1024, char>		fstring1024;
typedef fstring<1024, wchar_t>	fwstring1024;

} //namespace glf
#endif
